package edu.metrostate.capstonediner;

import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Random;

import android.content.SharedPreferences;
import android.preference.PreferenceManager;

public class Game extends Thread {
	private GameActivity activity;
	private GameRenderer renderer;
	private GameInput input;
	private GameAudio audio;
	private int score;
	private int itemsEaten;
	private int numPowerUps;
	private GameText scoreBox;
	private GameText hitPointsBox;
	private GameText bombTextBox;
	private GameText powerUpTextBox;
	private int bombHitPoints;
	private int lastTouchTally;
	
	//private GameText debugBox;
	
	private int mode;
	private float speed;  //measured in pixels per decisecond
	private Player player;
	private Background background;
	private GamePart powerUpPart;
	private PauseButton pauseButton;
	private SpecialEffect specialEffectPart;
	private ArrayList<GamePart> baddiesList;
	private boolean gameOver;
	//private boolean demoMovingRight;
	//currently makes no effort to limit framerate
	private long startTime;
	private long currentTime;
	private long lastFrameTime;
	private long pauseStartTime;
	private boolean isPaused;
	private boolean bombActive;
	private boolean funnelActive;
	private boolean pepperActive;
	private long bombStartTime;
	private long funnelStartTime;
	private long pepperStartTime;
	private final Object pauseLock = new Object();
	private static Random random = new Random();
	private PauseMenu pauseMenu;
	private SharedPreferences prefs;
	
	public Game(int mode, GameActivity activity, GameRenderer renderer, GameInput input, GameAudio audio) {
		//TODO: implement game modes other than "endless calorie"
		//also implement altsound
		
		//initialization
		this.activity = activity;
		this.renderer = renderer;
		this.input = input;
		pauseMenu = new PauseMenu(activity, this, input);
		this.audio = audio;
		
		prefs = PreferenceManager.getDefaultSharedPreferences(activity);
		
		gameOver = false;
		bombActive = false;
		funnelActive = false;
		pepperActive = false;
		score = 0;
		
		this.mode = mode;
		
		itemsEaten = 0;
		numPowerUps = 0;
		powerUpTextBox = new GameText("0", Constants.SCREEN_WIDTH - 16f, 44f, Constants.TEXT_LAYER, Constants.DEFAULT_FONT_SIZE, Constants.DEFAULT_FONT);
		renderer.add(powerUpTextBox);
		scoreBox = new GameText();
		renderer.add(scoreBox);
		hitPointsBox = new GameText("HP: 100", 0, Constants.SCREEN_HEIGHT - 32f, Constants.TEXT_LAYER, Constants.DEFAULT_FONT_SIZE, Constants.FONT2);
		renderer.add(hitPointsBox);
		//debugBox = new GameText("Touches: 0", 0, Constants.SCREEN_HEIGHT / 2f, Constants.TEXT_LAYER, Constants.DEFAULT_FONT_SIZE, Constants.FONT2);
		//renderer.add(debugBox);
		speed = 11f;  //arbitrary right now, will tune for best gameplay by difficulty
		background = new Background();
		renderer.add(background);
		player = new Player();
		if(prefs.getBoolean("altskin", false)) player.setTexture(Constants.SEDUCTIVE_LIPS_TEXTURE);
		renderer.add(player);
		if(mode == Constants.ENDLESS_CALORIE) {
			powerUpPart = new Funnel(Constants.SCREEN_WIDTH - 32f, 32f);
		}
		else if(mode == Constants.ENDLESS_DIET) {
			powerUpPart = new Finger(Constants.SCREEN_WIDTH - 32f, 32f);
		}
		renderer.add(powerUpPart);
		pauseButton = new PauseButton(Constants.SCREEN_WIDTH - 32f, 64f);
		renderer.add(pauseButton);
		baddiesList = new ArrayList<GamePart>(3);
		for(int c = 1; c <= 3; c++) {
			GamePart newBaddie = generateRandomPart();
			newBaddie.setX(c * (Constants.SCREEN_WIDTH / 4f));
			newBaddie.setY(c * 32f);
			baddiesList.add(newBaddie);
			renderer.add(newBaddie);
		}
		startTime = System.currentTimeMillis();
		currentTime = System.currentTimeMillis();
		lastFrameTime = System.currentTimeMillis() - 100l;
		pepperStartTime = 0l;
		bombStartTime =0l;
		funnelStartTime=0l;
		pauseStartTime = System.currentTimeMillis();
		
		isPaused = false;
		
		renderer.update();
		
	}
	
	public void startBombMiniGame() {
		speed = 0f;
		bombActive = true;
		bombStartTime = currentTime;
		if(itemsEaten < 1000)bombHitPoints = 10 + (itemsEaten / 20);
		else bombHitPoints = 60;
		bombTextBox = new GameText("Tap " + bombHitPoints + " times!", 0f, 120f, Constants.TEXT_LAYER, Constants.DEFAULT_FONT_SIZE, Constants.DEFAULT_FONT);
		renderer.add(bombTextBox);
		lastTouchTally = input.getTouchTally();
	}
	
	public void startSpicyPepper() {
		//TOFINISH
		pepperActive = true;
		specialEffectPart = new SpecialEffect(player.getX(), player.getY() -32f);
		renderer.add(specialEffectPart);
		pepperStartTime = System.currentTimeMillis();
	}
	
	public void gainMoney(int amount) {
		//TODO
	}
	
	public void gainPowerUp() {
		if(numPowerUps <5) numPowerUps++;
		powerUpTextBox.setText(Integer.toString(numPowerUps));
	}
	
	public void usePowerUp() {
		if(numPowerUps > 0 && !funnelActive && !bombActive && !pepperActive) {
			if(mode == Constants.ENDLESS_DIET) {
				score -= 1000000;
				itemsEaten -=10;
			} else if(mode == Constants.ENDLESS_CALORIE){
				funnelStartTime = currentTime;
				funnelActive = true;
				player.setX((Constants.SCREEN_WIDTH / 2f) - (player.getWidth() / 2f));
				powerUpPart = new Funnel((Constants.SCREEN_WIDTH / 2f) - 16f, player.getY() - 32f);
				renderer.add(powerUpPart);
			}
			numPowerUps--;
			powerUpTextBox.setText("" + numPowerUps);
		}
	}
	
	public void doGameOver() {
		audio.pauseAll(); //TODO:  destroy instead of pause
		for(int c = 0; c < baddiesList.size(); c++) {
			renderer.remove(baddiesList.get(c));
		}
		renderer.remove(player);
		renderer.remove(scoreBox);
		renderer.remove(hitPointsBox);
		renderer.add(new GameText("Game Over", 24f, 70f, Constants.TEXT_LAYER, 24, Constants.FONT1));
		renderer.add(new GameText("Your Score:", 24f, 264f, Constants.TEXT_LAYER, 20, Constants.FONT1));
		renderer.add(new GameText(NumberFormat.getNumberInstance(Locale.US).format(score) + "KCal", 24f, 288f, Constants.TEXT_LAYER, 16, Constants.FONT1));
		renderer.update();
		
		if (GameActivity.getUser() != null) {
			DatabaseHTTP.SaveScore(GameActivity.getUser(), score);
		}
		
		HighScoreScreen hs = new HighScoreScreen(activity, input);
		hs.show();
	}
	
	private GamePart generateRandomPart() {
		GamePart retPart;
		int value = Game.random.nextInt(26);
		switch(value) {
		case 0:
			retPart = new BellPepper();
			break;
		case 1:
			retPart = new Celery();
			break;		
		case 2:
			retPart = new Carrot();
			break;		
		case 3:
			retPart = new Corn();
			break;		
		case 4:
			retPart = new Apple();
			break;		
		case 5:
			retPart = new Banana();
			break;		
		case 6:
			retPart = new Popsicle();
			break;		
		case 7:
			retPart = new Candy();
			break;		
		case 8:
			retPart = new Fries();
			break;		
		case 9:
			retPart = new Potato();
			break;		
		case 10:
			retPart = new Fish();
			break;		
		case 11:
			retPart = new Hotdog();
			break;		
		case 12:
			retPart = new Burger();
			break;		
		case 13:
			retPart = new Ribs();
			break;		
		case 14:
			retPart = new Pizza();
			break;		
		case 15:
			retPart = new Turkey();
			break;		
		case 16:
			retPart = new Avocado();
			break;		
		case 17:
			retPart = new Bomb();
			break;		
		case 18:
			retPart = new CastorBean();
			break;		
		case 19:
			retPart = new SpicyPepper();
			break;		
		case 20:
			retPart = new Boot();
			break;		
		case 21:
			retPart = new PinkSpoon();
			break;		
		case 22:
			retPart = new PinkBottle();
			break;	
		case 23:
			retPart = new Pill();
			break;
		case 24:
			retPart = new PillBottle();
			break;
		case 25:
		default:
			//These items are classified as "super," and thus have a reduced probability of being spawned
			int superSuddenDeath = Game.random.nextInt(6);
			switch(superSuddenDeath){
			case 0:
				retPart = new OCIPS();
				break;
			case 1:
				retPart = new Horsey();
				break;
			case 2:
				retPart = new ButterSculpture();
				break;
			case 3:
				retPart = new SuperHealth();
				break;
			case 4:
				retPart = new Monies();
				break;
			case 5:
			default:
				if(mode == Constants.ENDLESS_CALORIE) retPart = new Funnel();
				else retPart = new Finger();
				break;
			}
			break;		

		}
		return retPart;
	}
	
	public void run() {
		//game loop
		while(!gameOver) {
			if(!bombActive) {
				currentTime = System.currentTimeMillis();
				parseInput();

				//when and if the background scrolls, that'll happen here

				for(int c = 0; c < baddiesList.size(); c++) {
					GamePart current = baddiesList.get(c);

					current.setY(current.getY() + ((float)(currentTime - lastFrameTime) / 100f) * speed);

					//hits end of screen
					if(current.getY() >= Constants.SCREEN_HEIGHT) {
						GamePart newBaddie = generateRandomPart();
						newBaddie.setX((Constants.SCREEN_WIDTH / 4f) * (c+1) - (newBaddie.getWidth() / 2f));
						renderer.remove(current);
						baddiesList.set(c, newBaddie);
						renderer.add(newBaddie);
					}
					//collision with player, needs streamlining
					if(!funnelActive) {
						if(current.getX() >= player.getX() - current.getWidth() && current.getX() <= player.getX() + player.getWidth() && current.getY() >= player.getY() - current.getHeight() && current.getY() <= player.getY() + player.getHeight() && !pepperActive) {
							current.collisionEffect(player, this);
							player.collisionEffect(current, this);
							if(player.getHitPoints() <= 0) {
								gameOver = true;
							} else hitPointsBox.setText("HP: " + player.getHitPoints());
							if(current.getHitPoints() <= 0) {
								GamePart newBaddie = generateRandomPart();
								newBaddie.setX((Constants.SCREEN_WIDTH / 4f) * (c+1) - (newBaddie.getWidth() / 2f));
								renderer.remove(current);
								baddiesList.set(c, newBaddie);
								renderer.add(newBaddie);
							}
							audio.play(Constants.CATCH_SOUND);
						}
					} else {
						if(current.getY() >= powerUpPart.getY() && current.getHitPoints() == 1) {
							current.collisionEffect(player, this);
							player.collisionEffect(current, this);
							hitPointsBox.setText("HP: " + player.getHitPoints());
							if(current.getHitPoints() <= 0) {
								GamePart newBaddie = generateRandomPart();
								newBaddie.setX((Constants.SCREEN_WIDTH / 4f) * (c+1) - (newBaddie.getWidth() / 2f));
								renderer.remove(current);
								baddiesList.set(c, newBaddie);
								renderer.add(newBaddie);
							}
							audio.play(Constants.CATCH_SOUND);
						}
					}
				}

				if(funnelActive && currentTime - funnelStartTime >= 10000l) {
					funnelActive = false;
					powerUpPart.setX(Constants.SCREEN_WIDTH - powerUpPart.getWidth());
					powerUpPart.setY(32f);
				}
				if(pepperActive && currentTime - pepperStartTime >= 2500l) {
					pepperActive = false;
					renderer.remove(specialEffectPart);
				}
				//speed increases by 1 every 20 seconds of play time, up to speed of (roughly) 40
				if(speed <= 40f) speed = 11f + ((currentTime - startTime) / 20000f);

				renderer.update();
				lastFrameTime = currentTime;
				synchronized (pauseLock) {
					while(isPaused) {
						try {pauseLock.wait();} catch (InterruptedException e){}
					}
				}
			} else {//bombActive is true
				if(bombHitPoints <= 0){
					bombActive = false;
					renderer.remove(bombTextBox);
					continue;
				}
				if(currentTime - bombStartTime >= 5000l){
					renderer.remove(bombTextBox);
					gameOver = true;
					continue;
				}
				bombHitPoints -= (input.getTouchTally() - lastTouchTally);
				lastTouchTally = input.getTouchTally();
				bombTextBox.setText("Tap " + bombHitPoints + " times!");
				lastFrameTime = currentTime; 
				currentTime = System.currentTimeMillis();
				renderer.update();
			}
		}
		doGameOver();
	}
	
	public void pause() {
		if(!isPaused) {
			audio.pauseAll();
			synchronized (pauseLock) {
				isPaused = true;
				pauseStartTime = System.currentTimeMillis();
			}
			pauseMenu.show();
		}
	}
	
	public void unPause() {
		if(isPaused) {

			if(prefs.getBoolean("mute", false)) audio.mute();
			else audio.unMute();
			if(prefs.getBoolean("altsound", false)) audio.setSoundSet(Constants.ALT_SOUNDSET);
			else audio.setSoundSet(Constants.DEFAULT_SOUNDSET);
			audio.unpauseAll();

			synchronized (pauseLock) {
				isPaused = false;
				lastFrameTime = System.currentTimeMillis();
				currentTime = lastFrameTime;
				startTime += currentTime - pauseStartTime;
				pepperStartTime += currentTime - pauseStartTime;
				funnelStartTime += currentTime - pauseStartTime;
				bombStartTime += currentTime - pauseStartTime;
				if(prefs.getBoolean("altskin", false)) player.setTexture(Constants.SEDUCTIVE_LIPS_TEXTURE);
				else player.setTexture(Constants.PLAYER_TEXTURE);

				pauseLock.notifyAll();
			}
		}
	}
	
	public void gainScore(int increment) {
		score += increment;
		itemsEaten++;
		scoreBox.setText(NumberFormat.getNumberInstance(Locale.US).format(score) + "Kcal");
	}
	
	private void parseInput() {
		if(input.getX() > Constants.SCREEN_WIDTH - 32f) {
			if(input.getY() >= 32f && input.getY() < 64f) {//power up button pressed
				usePowerUp(); 
				return;
			}
			if(input.getY() >= 64f && input.getY() < 96f) {//pause button pressed
				pause();
			}
		}
		if(!funnelActive) player.setX(input.getX() - (player.width / 2f));
		if(pepperActive) specialEffectPart.setX(player.getX());
	}
	/*
	private void parseInputDemo() {
		//DEPRECATED:  based on coordinates pre-rev.21
		//currently just a demo, moves the player back and forth across the bottom of the screen
		float offset = ((float)(currentTime - lastFrameTime) / 100f) * speed;
		if(!demoMovingRight){
			if(player.getX() < (player.getWidth() / 2f) + offset) {
				player.setX(offset - (player.getX() - player.getWidth() / 2f));
				demoMovingRight = true;
			} 
			else {
				player.setX(player.getX() - offset);
			}
		}
		else {
			if(player.getX() > (Constants.SCREEN_WIDTH - (player.getWidth() / 2f)) - offset) {
				//move the player to the difference between their distance from the left extremum and the offset 
				player.setX(2 * (Constants.SCREEN_WIDTH - (player.getWidth() / 2f)) - offset -  player.getX());
				demoMovingRight = false;
			}
			else {
				player.setX(player.getX() + offset);
			}
		}
	}*/
	
	
}
